#ifndef WARRAY_TESTS_HELPER_H_INCLUDED
#define WARRAY_TESTS_HELPER_H_INCLUDED

#include <limits>
#include <cmath>
#include <boost/test/floating_point_comparison.hpp>

template<size_t N>
warp::array_extents<N> extents_gen(size_t d)
{
	return extents_gen<N-1>(d)[d];
}

template<>
inline warp::array_extents<0> extents_gen<0>(size_t d)
{
	return warp::array_extents<0>();
}

template<typename T, size_t N>
void fill_nonzero(const warp::arraynd<T,N>& x)
{
	int val=1;
	for(typename warp::arraynd<T,N>::iterator it=x.begin();
		it!=x.end();
		++it)
	{
		*it = T(val++);
	}
}

template<typename T, size_t N>
void fill_nonzero(const warp::arraynd<std::complex<T>,N>& x)
{
	int val=1;
	for(typename warp::arraynd<std::complex<T>,N>::iterator it=x.begin();
		it!=x.end();
		++it)
	{
		int v1 = val++;
		int v2 = val++;
		*it = std::complex<T>(T(v1),T(v2));
	}
}

template<typename T>
bool equal_tol(const T& l, const T& r)
{
	return l == r;
}

inline bool equal_tol(const float& l, const float& r)
{
	if(l == 0)
		return std::abs(r) < std::numeric_limits<float>::epsilon();
	else if(r == 0)
		return std::abs(l) < std::numeric_limits<float>::epsilon();
	else
		return boost::test_tools::check_is_close(l,r,boost::test_tools::fraction_tolerance(0.0001));
}

inline bool equal_tol(const double& l, const double& r)
{
	/*
	check_is_close will always fail if one value is exactly zero and the other is not, for 
	doubles we assume the calculation will deliver the correct result even in these difficult
	circumstances.

	if(l == 0)
		return std::abs(r) < std::numeric_limits<double>::epsilon();
	else if(r == 0)
		return std::abs(l) < std::numeric_limits<double>::epsilon();
	else
	*/
	return boost::test_tools::check_is_close(l,r,boost::test_tools::fraction_tolerance(0.0001));
}

template<class T>
bool equal_tol(const std::complex<T>& l, const std::complex<T>& r)
{
	return equal_tol(l.real(),r.real()) && equal_tol(l.imag(),r.imag());
}

template<typename T, size_t N, typename EL_OP>
bool array_equal(const warp::arraynd<T,N>& x, const warp::arraynd<T,N>& y, const warp::arraynd<T,N>& z, EL_OP eop)
{
	assert(x.size() == y.size());
	if(x.size() != z.size())
		return false;

	for(typename warp::arraynd<T,N>::iterator xit=x.begin(),yit=y.begin(),zit=z.begin();
		xit!=x.end();
		++xit,++yit,++zit)
	{
		if( !equal_tol(eop(*xit,*yit), *zit) )
		{
			return false;
		}
	}

	return true;
}

template<typename T1, typename T2, size_t N, typename EL_OP>
bool array_equal(const warp::arraynd<T1,N>& x, const warp::arraynd<T2,N>& y, EL_OP eop)
{
	if(x.size() != y.size())
		return false;

	typename warp::arraynd<T1,N>::iterator xit;
	typename warp::arraynd<T2,N>::iterator yit;
	for(xit=x.begin(),yit=y.begin();
		xit!=x.end();
		++xit,++yit)
	{
		if( !equal_tol(eop(*xit), *yit) )
			return false;
	}

	return true;
}


#endif


